🔥 algorithm | February 17, 2021
선형 자료구조란?
데이터 요소가 순차적으로 배열되는 자료구조를 선형(Linear) 자료구조라고 한다. 선형 자료구조는 단일 레벨로 구성된다. 따라서 한 번에 탐색이 가능하다. 배열, 스택, 큐, 연결 리스트 등이 모두 선형 자료형에 속한다.
배열은 값 또는 변수 엘리먼트의 집합으로 구성된 구조로, 하나 이상의 인덱스 또는 키로 식별된다.
배열은 연속 방식이 기본이 되는 자료형
파이썬의list는 동적 배열 자료형
크기를 자동으로 리사이징하는 배열
대부분의 언어에서 동적 배열 자료형을 제공한다.
더블링(Doubling)이라 하며, 2배씩 늘려나간다.파이썬의 더블링은 초반에 2배씩 늘려나가지만, 뒤로 갈 수록 재할당하는 식의 방식으로 간극을 좁힌다.
그로스 팩터(Growth Factor)라고 하며, 성장 인자라고도 불린다.덧셈하여 타켓을 만들 수 있는 배열의 두 숫자 인덱스를 리턴하라.
def twoSum(nums, target):
for i in range(len(nums)):
for j in range(i + 1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
print(twoSum([2, 7, 11, 15], 9))def twoSum(nums, target):
for i, n in enumerate(nums):
complement = target - n
if complement in nums[i + 1:]:
return nums.index(n), nums[i + 1:].index(complement) + (i + 1)
print(twoSum([2, 7, 11, 15], 9))target - n이 존재하는지 확인in의 시간 복잡도는 O(n)으로 이전과 동일한 O(n^2)지만, 같은 시간 복잡도라면 in이 더 빠르다.def twoSum(nums, target):
nums_map = {}
# 키와 값을 바꿔서 딕셔너리에 저장
for i, num in enumerate(nums):
nums_map[num] = i
# 타겟에서 첫 번째 수를 뺀 결과를 키로 조회
for i, num in enumerate(nums):
if target - num in nums_map and i != nums_map[target - num]:
return nums.index(num), nums_map[target - num]
print(twoSum([2, 7, 11, 15], 9))target - n 한 값을 dict.key로 찾는 방법def twoSum(nums, target):
nums_map = {}
# 하나의 for 문으로 통합
for i, num in enumerate(nums):
if target - num in nums_map:
return [nums_map[target - num], i]
nums_map[num] = i
print(twoSum([2, 7, 11, 15], 9))def twoSum(nums, target):
left, right = 0, len(nums) - 1
while not left == right:
# 합이 타겟보다 작으면 왼쪽 포인터를 왼쪽으로
if nums[left] + nums[right] < target:
left += 1
# 합이 타겟보다 크면 오른쪽 포인터를 왼쪽으로
elif nums[left] + nums[right] > target:
right -= 1
else:
return left, right
print(twoSum([2, 7, 11, 15], 9))이 문제의 문제점은 정렬이 되어 있는 상태를 기대해야 하고, 값이 아닌 index를 찾아 내는 것이라면 재정렬했을 때 index가 바뀌는 문제점이 발생된다.
높이를 입력받아 비 온 후 얼마나 많은 물이 쌓일 수 있는지 계산하라.
def trap(height):
if not height:
return 0
volume = 0
left, right = 0, len(height) - 1
left_max, right_max = height[left], height[right]
while left < right:
left_max, right_max = max(height[left], left_max), \
max(height[right], right_max)
# 더 높은 쪽을 향해 투 포인터로 이동
# 가장 높은 곳에 왼쪽/오른쪽 모두 도착하면 return
# 현재 높이와의 차이만큼 물 높이 `volume`을 더해 나간다.(낮은 쪽은 그만큼 항상 채워짐)
if left_max <= right_max:
volume += left_max - height[left]
left += 1
else:
volume += right_max - height[right]
right -= 1
return volume
print(trap([0,1,0,2,1,0,1,3,2,1,2,1]))volume += left_max - height[left] volume += right_max - height[right])은 물이 항상 채워지므로 그 수의 차이만큼 volume을 더해 나간다.def trap(height):
stack = []
volume = 0
for i in range(len(height)):
# 변곡점을 만나는 경우(현재 높이가 이전 높이보다 높을 경우에만)
while stack and height[i] > height[stack[-1]]:
# 스택에서 꺼낸다
top = stack.pop()
if not len(stack):
break
# 이전과의 차이만큼 물 높이 처리
distance = i - stack[-1] - 1 # 현재 위치랑 전 위치 간, 즉 변곡점 간의 거리 차이
waters = min(height[i], height[stack[-1]]) - height[top] # 쌓인 물의 양
volume += distance * waters
stack.append(i)
return volume
print(trap([0,1,0,2,1,0,1,3,2,1,2,1]))배열을 입력받아 합으로 0을 만들 수 있는 3개의 엘리먼트를 출력하라.
def threeSum(nums):
result = []
nums.sort()
# 브루투 포스 n^3 반복
for i in range(len(nums) - 2):
# 중복된 값 건너뛰기
if i > 0 and nums[i] == nums[i - 1]:
continue
for j in range(i + 1, len(nums) - 1):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
for k in range(j + 1, len(nums)):
if k > j + 1 and nums[k] == nums[k -1]:
continue
if nums[i] + nums[j] + nums[k] == 0:
result.append((nums[i], nums[j], nums[k]))
return result
print(threeSum([-1, 0, 1, 2, -1, -4]))앞뒤로 같은 값이 있을 경우, 이를 쉽게 처리하기 위해 sort()
O(n log n)중복된 값이 있을 수 있으므로 continue로 분기 처리 j > i+1처럼 +1를 해준 이유는, 앞에 if 조건문에서 앞뒤로 이미 체크했기 때문에, 한칸 더 간 인덱스부터 처리하기 위함
def threeSum(nums):
result = []
nums.sort()
for i in range(len(nums) - 2):
# 중복된 값 건너뛰기
if i > 0 and nums[i] == nums[i - 1]:
continue
# 간격을 좁혀가며 합 sum 계산 (재반복 할 때 마다 left 값이 +1 된 상태로 초기화)
left, right = i + 1, len(nums) - 1
while left < right:
sum = nums[i] + nums[left] + nums[right]
if sum < 0:
left += 1
elif sum > 0:
right -= 1
else:
# sum = 0인 경우이므로 정답 및 스킵 처리
result.append((nums[i], nums[left], nums[right]))
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
return result
print(threeSum([-1, 0, 1, 2, -1, -4]))0 값을 찾아내 append 시키며, left, right 증가/감소를 반복하며 답을 찾아낸다.투 포인터란?
일반적으로 시작점과 끝점 또는 왼쪽 포인터와 오른쪽 포인터 두 지점을 기준으로 하는 문제 풀이 전략을 뜻하다. 범위를 좁혀 나가기 위해서는, 일반적으로 배열이 정렬되어 있을 때 유용하다.